package structure

import (
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/serialize"
)

//go:generate msgp

type PublicDataType int

const (
	ScheduleDataType string = "schedule"
	TaskDataType            = "task"
	MapDataType             = "map"
	EventDataType           = "event"
)

const (
	ScheduleType PublicDataType = 126
	TaskType     PublicDataType = 127
	MapType      PublicDataType = 128
)

//msgp ExtendType
type ExtendType string

type EventMode byte

type ExtendMode byte

const (
	ListExtendMode ExtendMode = iota
	SetExtendMode
	SortedExtendMode
)

type Relation string

type ScheduleLogic byte

type TaskEvent byte

type DateFactory interface {
	CurrentTime() int64
	CurrentSecond() int
	FormatTime(delay int64) int64
	FormatTimeFrom(delay int64, from int64) int64
	FormatSecond(delay int) int
	FromStartUp(delay int64) int
	GetStartUp() int64
}

type Data interface {
	GetDataType() PublicDataType
	GetId() interface{}
	Bytes() []byte
	GetItem() []byte
	Exist() bool
}

//msgp:tuple BaseData
type BaseData struct {
	DataType PublicDataType `json:"data_type" msg:"data_type"`
	Id       interface{}    `json:"id" msg:"id"`
}

func (base *BaseData) Equal(data Data) bool {
	//fmt.Println("equal", data.GetDataType(), base.DataType, data.GetDataType() == base.DataType, utils.ToString(data.GetId()), utils.ToString(base.Id), utils.ToString(data.GetId()) == utils.ToString(base.Id))
	return data.GetDataType() == base.DataType && utils.ToString(data.GetId()) == utils.ToString(base.Id)
}

func (base *BaseData) GetDataType() PublicDataType {
	return base.DataType
}
func (base *BaseData) GetId() interface{} {
	return base.Id
}
func (base *BaseData) GetItem() []byte {
	return base.Bytes()
}
func (base *BaseData) Bytes() []byte {
	tmp := base.Id
	base.Id = utils.ToString(tmp)
	b, _ := base.MarshalMsg(nil)
	base.Id = tmp
	return b
}
func (base *BaseData) Exist() bool {
	return base != nil
}
func NewBaseData(dataType PublicDataType, id interface{}) *BaseData {
	return &BaseData{
		DataType: dataType,
		Id:       id,
	}
}
func NewBaseDataFrom(dataType Data) *BaseData {
	if dataType == nil {
		return nil
	}
	if !dataType.Exist() {
		return nil
	}
	return &BaseData{
		DataType: dataType.GetDataType(),
		Id:       utils.Copy(dataType.GetId()),
	}
}
func NewBaseDataString(item string) *BaseData {
	b := utils.ToBytes(item)
	v := &BaseData{}
	_, _ = v.UnmarshalMsg(b)
	return v
}
func NewBaseDataByte(b []byte) *BaseData {
	if b == nil || len(b) == 0 {
		return nil
	}
	v := &BaseData{}
	_, _ = v.UnmarshalMsg(b)
	if v.DataType == 0 {
		return nil
	}
	return v
}

func NewBaseDataBy(obj interface{}) *BaseData {
	if obj == nil {
		return nil
	}
	switch t := obj.(type) {
	case *BaseData:
		return t
	case []byte:
		return NewBaseDataByte(t)
	case string:
		return NewBaseDataString(t)
	case Data:
		return NewBaseDataFrom(t)
	}
	v := &BaseData{}
	os := utils.ToInterfaceSlice(obj)
	if os == nil {
		return nil
	}
	v.DataType = PublicDataType(utils.ToInt(os[0]))
	v.Id = utils.Copy(os[1])
	return v
}

//msgp:tuple SortedData
type SortedData struct {
	Value interface{} `json:"value" msg:"value"`
	Score int         `json:"score" msg:"score"`
}

func NewSortedDataByte(b []byte) *SortedData {
	if b == nil || len(b) == 0 {
		return nil
	}
	v := &SortedData{}
	_, _ = v.UnmarshalMsg(b)
	return v
}

func (sd *SortedData) Bytes() []byte {
	b, _ := sd.MarshalMsg(nil)
	return b
}

type SortedDataSlice []*SortedData

func (slice SortedDataSlice) ToList() [][]byte {
	m := make([][]byte, len(slice))
	for index, sd := range slice {
		b, _ := sd.MarshalMsg(nil)
		m[index] = b
	}
	return m
}

//msgp:tuple UpdateExtend
type UpdateExtend struct {
	Index int         `json:"index" msg:"index"`
	Value interface{} `json:"value" msg:"value"`
}

//msgp:tuple EventData
type EventData struct {
	Mode    EventMode `json:"mode" msg:"mode"`
	Content []byte    `json:"content" msg:"content"`
	Event   byte      `json:"event" msg:"event"`
}

func (ed *EventData) UnSerialize(data []byte) error {
	_, err := ed.UnmarshalMsg(data)
	return err
}

//msgp:tuple ScheduleInfo
type ScheduleInfo struct {
	MapNoId  string
	Schedule ScheduleLogic
	From     *BaseData
	Target   *BaseData
	Key      string
	Time     int64
	Data     []byte
	Params   ParamSlice
	//TimeList   TimeSlice
	Version       int
	ScheduleDelay int

	ScheduleData BaseData       `json:"-" msg:"-"`
	Depend       string         `json:"-" msg:"-"`
	Slot         serialize.Slot `json:"-" msg:"-"`
}

func (info *ScheduleInfo) Release() {
	if info.Slot != nil {
		info.Slot.Release()
		info.Slot = nil
	}
}

func (info *ScheduleInfo) GetFrom() *BaseData {
	return info.From
}
func (info *ScheduleInfo) GetTarget() *BaseData {
	return info.Target
}
func (info *ScheduleInfo) GetTime() int64 {
	return info.Time
}
func (info *ScheduleInfo) GetMapNoId() string {
	return info.MapNoId
}

//msgp:tuple TaskInfo
type TaskInfo struct {
	MapNoId  string
	From     *BaseData
	Task     TaskEvent
	Content  []byte
	Time     int64
	Params   ParamSlice
	Callback []byte

	Sid string `json:"-" msg:"-"`
}

//msgp ParamSlice
type ParamSlice []interface{}

//msgp TimeSlice
type TimeSlice []int64

type IntList []uint8

func NewIntList(i int) IntList {
	return make([]uint8, i)
}

func NewIntListInt(i int) IntList {
	b := utils.ToBytes(i)
	return NewIntListByte(b)
}
func NewIntListByte(b []byte) IntList {
	if b == nil || len(b) == 0 {
		return nil
	}
	v := IntList{}

	return v
}
func NewIntListString(item string) IntList {
	b := utils.ToBytes(item)
	return NewIntListByte(b)
}
func NewIntListBy(obj interface{}) IntList {
	if obj == nil {
		return nil
	}
	switch t := obj.(type) {
	case IntList:
		return t
	case int:
		return NewIntListInt(t)
	case []byte:
		return NewIntListByte(t)
	case string:
		return NewIntListString(t)
	}
	v := utils.ToIntSlice(obj)
	tt := make([]uint8, len(v))
	for index, vv := range v {
		if vv > 100 {
			vv = 100
		} else if vv < 0 {
			vv = 0
		}
		tt[index] = uint8(vv)
	}
	return tt
}
func (list IntList) Change(c []int) {
	for index, vv := range c {
		if index >= len(list) {
			break
		}
		v := list[index]
		vv += int(v)
		if vv > 100 {
			vv = 100
		} else if vv < 0 {
			vv = 0
		}
	}
}
